﻿using System;
using System.Linq;
using EaseDapper.Interface;
using System.Collections.Concurrent;

namespace EaseDapper
{
    public class InstanceFactory
    {
        #region Fields
        private static readonly ConcurrentDictionary<string, Type> TypeCache = new ConcurrentDictionary<string, Type>();
        #endregion

        #region Get Builder
        internal static IBuilder CreateBuilderExpressions(string name)
        {
            string className = CreatBuilderClassName(name);
            IBuilder builder = Activator.CreateInstance(Type.GetType(className), true) as IBuilder;
            return builder;
        }

        private static string CreatBuilderClassName(string name)
        {
            return string.Concat(GlobalDatas.AssemblyName, ".", name, "BuilderExpression");
        }
        #endregion

        #region CreatAbstractFactory
        internal static IAbstractFactory GetAbstractFactory()
        {
            string className = CreatFactoryClassName();
            IAbstractFactory result = Activator.CreateInstance(Type.GetType(className), true) as IAbstractFactory;
            return result;
        }

        private static string CreatFactoryClassName()
        {
            return string.Concat(GlobalDatas.AssemblyName, ".", GlobalDatas.Config.DbType, "ExpressionFactory");
        }
        #endregion

        #region CreatQueryExpressions
        internal static Result GetQueryExpressions<T, Result>()
        {
            string className = CreatQueryClassName(GlobalDatas.Config.DbType);
            return CreateInstance<Result>(className, typeof(T));
        }

        private static string CreatQueryClassName(string type)
        {
            return string.Concat(GlobalDatas.AssemblyName, ".", type, "QueryExpressions");
        }
        #endregion

        #region CreatUpdateExpressions
        internal static Result GetUpdateExpressions<T, Result>()
        {
            string className = CreatUpdateClassName(GlobalDatas.Config.DbType);
            return CreateInstance<Result>(className, typeof(T));
        }

        private static string CreatUpdateClassName(string type)
        {
            return string.Concat(GlobalDatas.AssemblyName, ".", type, "UpdateExpressions");
        }
        #endregion

        #region CreatDeleteExpressions
        internal static Result GetDeleteExpressions<T, Result>()
        {
            string className = CreatDeleteClassName(GlobalDatas.Config.DbType);
            return CreateInstance<Result>(className, typeof(T));
        }

        private static string CreatDeleteClassName(string type)
        {
            return string.Concat(GlobalDatas.AssemblyName, ".", type, "DeleteExpressions");
        }
        #endregion

        #region Private Method
        private static Restult CreateInstance<Restult>(string className, params Type[] types)
        {
            string cacheKey = className + string.Join(",", types.Select(it => it.FullName));
            Type type;
            if (TypeCache.ContainsKey(cacheKey))
            {
                type = TypeCache[cacheKey];
            }
            else
            {
                lock (TypeCache)
                {
                    type = Type.GetType(className + "`" + types.Length, true).MakeGenericType(types);
                    if (!TypeCache.ContainsKey(cacheKey))
                    {
                        TypeCache.TryAdd(cacheKey, type);
                    }
                }
            }
            Restult result = (Restult)Activator.CreateInstance(type, true);
            return result;
        }
        #endregion
    }
}
